//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#include "NjExample01.h"
#include "NjMutexTest.h"
#include "NjSynchronizationTest.h"
#include "NjSharedSyncObjectTest.h"
#include "NjDebugSystemTestImplementation.h"
#include "NjSharedSyncObjectConstants.h"
#include "NjThreadsSystem.h"
#include "NjTestUnit.h"

//----------------------------------------------------------------------------
class NjMutexTest::Static
{
public:
	static void RunMutexAssert();
	static void RunMutexAssertThread01(void * pData);

#if NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	class MutexAssertSharedData02
	{
	public:
		NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(MutexAssertSharedData02);
		NJ_TRIVIAL_DESTRUCTOR(MutexAssertSharedData02);

		NjMutex m_kMutexA;
		NjMutex m_kMutexB;
		NjSemaphore m_kWait;
		NjBool m_bSuccess;
		NjBool m_bCompleted;
		NjBool m_bWasTriggeredBreakpoint;
		NjDebugSystemTestImplementation * m_pTestBreakpoint;

		static void RunThread(void * pData);

	private:
		NJ_DISABLE_COPY(MutexAssertSharedData02);
	};
#endif // NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED

	NJ_DISABLE_INSTANCE(Static);
};
//----------------------------------------------------------------------------
void NjMutexTest::Run(void)
{
	NjSharedSyncObjectTest<NjMutex>::RunFunctional();
	NjSharedSyncObjectTest<NjMutex>::RunFunctionalMultithreaded();
	NjSharedSyncObjectTest<NjMutex>::RunPerformances();
	Static::RunMutexAssert();
	NjSharedSyncObjectTest<NjMutex>::RunLocker();
}
//----------------------------------------------------------------------------
void NjMutexTest::Static::RunMutexAssert()
{
#if NJ_MUTEX_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjMutex>::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (destruction)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			NjThreadsSystem::Thread * pThread;

			{
				NjMutex kSyncObject;
				kSyncObject.Lock();
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				pThread=NjThreadsSystem::CreateThread(RunMutexAssertThread01, &kSyncObject);

				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(NjThreadsSystem::Sleep(NjSharedSyncObjectConstants::TIME_GRANULARITY));
			}

			NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());

			NJ_TEST(NjThreadsSystem::TryJoinThread(pThread, NjSharedSyncObjectConstants::TIME_GRANULARITY*5)==NjEventsSystem::TRY_SUCCESS);
			NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_MUTEX_STORE_USERS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED

#if NJ_MUTEX_STORE_LOCKER_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjMutex>::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (destruction)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			{
				NjMutex kSyncObject;
				kSyncObject.Lock();
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());
			}

			NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();

		NJ_TEST_UNIT("Assertion (double lock)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			{
				NjMutex kSyncObject;
				kSyncObject.Lock();
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());
				kSyncObject.Lock();
				NJ_TEST(pTestBreakpoint->WasTriggeredBreakpoint());
				kSyncObject.Unlock();
			}

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_MUTEX_STORE_LOCKER_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED

#if NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	NJ_TEST_UNITS_GROUP(NjSharedSyncObjectConstants::Query<NjMutex>::ms_szSyncObject);
		NJ_TEST_UNIT("Assertion (deadlocks)");
			NjSmartPtr<NjDebugSystem::Implementation> spImplementation(NjDebugSystem::GetImplementation());
			NjDebugSystemTestImplementation * const pTestBreakpoint(NjNew(NjDebugSystemTestImplementation(spImplementation)));
			NJ_TEST(pTestBreakpoint!=NJ_NULL);
			NjDebugSystem::SetImplementation(pTestBreakpoint);

			{
				NjThreadsSystem::Thread * pThread;
				MutexAssertSharedData02 kSharedData;
				kSharedData.m_bCompleted=NJ_FALSE;
				kSharedData.m_bWasTriggeredBreakpoint=NJ_FALSE;
				kSharedData.m_pTestBreakpoint=pTestBreakpoint;

				kSharedData.m_kMutexA.Lock();
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				pThread=NjThreadsSystem::CreateThread(MutexAssertSharedData02::RunThread, &kSharedData);
				NJ_TEST(pThread!=NJ_NULL);
				NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

				kSharedData.m_kWait.Lock();
				NjBool const bSuccess(kSharedData.m_kMutexB.TryLock(NjEventsSystem::WAIT_FOREVER));
				NjBool bWasTriggeredBreakpoint;

				if (bSuccess)
				{
					kSharedData.m_kMutexB.Unlock();
					bWasTriggeredBreakpoint=NJ_FALSE;
				}
				else
					bWasTriggeredBreakpoint=pTestBreakpoint->WasTriggeredBreakpoint();

				kSharedData.m_kMutexA.Unlock();
				NJ_TEST(NjThreadsSystem::TryJoinThread(pThread, NjSharedSyncObjectConstants::TIME_GRANULARITY*5)==NjEventsSystem::TRY_SUCCESS);
				NJ_TEST(NjThreadsSystem::DestroyThread(pThread));

				NJ_TEST(kSharedData.m_bCompleted);
				NJ_TEST(bSuccess!=kSharedData.m_bSuccess);
				NJ_TEST(bSuccess!=bWasTriggeredBreakpoint);
				NJ_TEST(kSharedData.m_bSuccess!=kSharedData.m_bWasTriggeredBreakpoint);
			}

			NJ_TEST(!pTestBreakpoint->WasTriggeredBreakpoint());

			NjDebugSystem::SetImplementation(spImplementation);
		NJ_TEST_UNIT_END();
	NJ_TEST_UNITS_GROUP_END();
#endif // NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
}
//----------------------------------------------------------------------------
void NjMutexTest::Static::RunMutexAssertThread01(void * pData)
{
	NjMutex * const pSyncObject(reinterpret_cast<NjMutex *>(pData));
	pSyncObject->TryLock(NjSharedSyncObjectConstants::TIME_GRANULARITY*3);
}
//----------------------------------------------------------------------------
#if NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
	void NjMutexTest::Static::MutexAssertSharedData02::RunThread(void * pData)
	{
		MutexAssertSharedData02 * const pSharedData(reinterpret_cast<MutexAssertSharedData02 *>(pData));
		if (pSharedData==NJ_NULL)
			return;

		pSharedData->m_kMutexB.Lock();
		pSharedData->m_kWait.Unlock();
		pSharedData->m_bSuccess=pSharedData->m_kMutexA.TryLock(NjEventsSystem::WAIT_FOREVER);

		if (pSharedData->m_bSuccess)
			pSharedData->m_kMutexA.Unlock();
		else
			pSharedData->m_bWasTriggeredBreakpoint=pSharedData->m_pTestBreakpoint->WasTriggeredBreakpoint();

		pSharedData->m_kMutexB.Unlock();
		pSharedData->m_bCompleted=NJ_TRUE;
	}
#endif // NJ_MUTEX_DETECT_DEADLOCKS_ENABLED && NJ_DEBUG_SYSTEM_ENABLED && NJ_ASSERT_ENABLED
//----------------------------------------------------------------------------
